//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;

namespace OpenLS.UI.Interop
{
    internal class VistaWindowMessageProcessor : WindowMessageProcessor
    {
        private YAndHeight? _resizeTop;

        public VistaWindowMessageProcessor(RibbonWindow w)
            : base(w)
        {
        }

        internal override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch ((WindowsMessages) msg)
            {
                case WindowsMessages.ActivateApp:
                case WindowsMessages.Activate:
                    _owner.AdjustMargins();
                    break;

                case WindowsMessages.NcHitTest:
                    return (IntPtr) (int) onNcHitTest(hwnd, msg, wParam, lParam, ref handled);
                case WindowsMessages.NcCalcSize:
                    return onNcCalcSize(hwnd, msg, wParam, lParam, ref handled);
                case WindowsMessages.GetMinMaxInfo:
                    return OnGetMinMaxInfo(hwnd, msg, wParam, lParam, ref handled);
                case WindowsMessages.LButtonDown:
                    return onLButtonDown(msg, hwnd, wParam, lParam, out handled);
                case WindowsMessages.LButtonUp:
                    return onLButtonUp(msg, hwnd, wParam, lParam, out handled);
                case WindowsMessages.ThemeChanged:
                    _owner.IsGlass = NativeMethods.IsCompositionEnabled();
                    break;
                case WindowsMessages.MouseMove:
                    if (_resizeTop != null)
                    {
                        NativeMethods.POINT pt2;
                        NativeMethods.GetCursorPos(out pt2);
                        NativeMethods.SetWindowPos(hwnd, IntPtr.Zero, (int) _owner.Left, pt2.Y, (int) _owner.ActualWidth,
                                                   (int) (_resizeTop.Value.Height + _resizeTop.Value.Y - pt2.Y),
                                                   NativeMethods.SWP_NOOWNERZORDER & NativeMethods.SWP_NOZORDER);
                        //\\ Top = pt2.Y;
                        //\\Height = _resizeTop.Value.Height + _resizeTop.Value.Y - pt2.Y;
                        handled = true;
                        return IntPtr.Zero;
                    }
                    break;
                case WindowsMessages.LButtonDblClk:
                case WindowsMessages.NcLButtonDblClk:
                    if (getHitTest(hwnd, lParam) == HitTest.Caption)
                        _owner.ToggleMaximized();
                    handled = true;
                    return IntPtr.Zero;
            }

            return (IntPtr) 0;
        }

        private IntPtr onLButtonUp(int msg, IntPtr hwnd, IntPtr wParam, IntPtr lParam, out bool handled)
        {
            var pt = new NativeMethods.POINT((int) lParam);
            _resizeTop = null;
            if (Mouse.Captured == _owner)
            {
                _owner.ReleaseMouseCapture();
            }
            HitTest ptr2 = getHitTest(hwnd, (IntPtr) pt.ToInt32());
            IntPtr result = IntPtr.Zero;
            switch (ptr2)
            {
                case HitTest.Close:
                    _owner.Close();
                    handled = true;
                    return result;
                case HitTest.Zoom:
                    _owner.ToggleMaximized();
                    handled = true;
                    return result;
                case HitTest.MinButton:
                    _owner.WindowState = WindowState.Minimized;
                    handled = true;
                    return result;
            }
            handled = false;
            return NativeMethods.DefWindowProc(hwnd, msg, wParam, lParam);
        }

        private IntPtr onLButtonDown(int msg, IntPtr hwnd, IntPtr wParam, IntPtr lParam, out bool handled)
        {
            HitTest ptr1 = getHitTest(hwnd, lParam);
            handled = false;
            IntPtr result = IntPtr.Zero;
            switch (ptr1)
            {
                case HitTest.Caption:
                    //\\ Owner.DragMove();
                    return result;
                case HitTest.Top:
                    _owner.CaptureMouse();
                    _resizeTop = new YAndHeight(_owner.PointToScreen(Mouse.GetPosition(_owner)).Y, _owner.ActualHeight);
                    break;
                case HitTest.NoWhere:
                    break;
                case HitTest.MinButton:
                case HitTest.MaxButton:
                case HitTest.Close:
                    result = NativeMethods.SendMessage(hwnd, (int) WindowsMessages.NclButtonDown, IntPtr.Zero,
                                                       (int) ptr1);
                    handled = true;
                    return result;
            }
            return NativeMethods.DefWindowProc(hwnd, msg, wParam, lParam);
        }

        private HitTest getHitTest(IntPtr hwnd, IntPtr lParam)
        {
            var pt = new NativeMethods.POINT(lParam.ToInt32());
            Point pt2 = pt.ToPoint();
            pt2 = _owner.PointToScreen(pt2);
            pt = new NativeMethods.POINT(pt2);
            int lp = pt.ToInt32();
            bool b = false;
            //HitTest ptr = onNcHitTest(hwnd, (int) WindowsMessages.NcHitTest, IntPtr.Zero, (IntPtr) lp, ref b);
            HitTest result = (HitTest) NativeMethods.SendMessage(hwnd, (int) WindowsMessages.NcHitTest, IntPtr.Zero, (IntPtr) lp);
            return result;
        }


        private static IntPtr onNcCalcSize(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            RECT originalRect = NativeMethods.NcCalcSizeParams.GetFrom(lParam).rgrc0;
            NativeMethods.DefWindowProc(hwnd, msg, wParam, lParam);
            if (wParam == new IntPtr(1))
            {
                NativeMethods.NcCalcSizeParams calcSizeParams = NativeMethods.NcCalcSizeParams.GetFrom(lParam);
                RECT border = NativeMethods.GetBorderRect(hwnd);
                if (NativeMethods.IsMaximized(hwnd))
                {
                    RECT r0 = originalRect;
                    r0.Left += border.Left;
                    r0.Right -= border.Right;

                    r0.Bottom += originalRect.Top;
                    r0.Bottom -= 1;
                    r0.Top = originalRect.Top + 0;
                    calcSizeParams.rgrc0 = r0;
                }
                else
                {
                        calcSizeParams.rgrc0.Top = originalRect.Top;
                }
                Marshal.StructureToPtr(calcSizeParams, lParam, false);
                handled = true;

                return IntPtr.Zero;
            }
            Contract.Assert(false, "nc calc size");
            return IntPtr.Zero;
        }

        private HitTest onNcHitTest(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            handled = false;
            var pt = new NativeMethods.POINT((int) lParam);
            var result = (HitTest) (int) NativeMethods.CallDwmBase(hwnd, msg, wParam, lParam);
            if (result == HitTest.NoWhere)
            {
                Point pt2 = pt.ToPoint();
                {
                    try
                    {
                        pt2 = _owner.PointFromScreen(pt2);
                    }
                    catch (InvalidOperationException)
                    {
                        return result;
                    }
                    
                }
                if (_owner.ApplicationMenu != null)
                {
                    try
                    {
                        Point ptAppMenu = _owner.ApplicationMenu.PointFromScreen(pt2);
                        if (ptAppMenu.X <= _owner.ApplicationMenu.ActualWidth && pt.X > 0)
                        {
                            return HitTest.NoWhere;
                        }
                    }
                    catch (InvalidOperationException)
                    {
                        return result;
                    }
                    
                   
                }
                double y = pt2.Y;
                if (y < SystemParameters.BorderWidth)
                {
                   // result = HitTest.Top;
                    handled = true;
                    return HitTest.Top;
                }
                else if (y <= _owner.CaptionHeight)
                    result = HitTest.Caption;
                if (result == HitTest.NoWhere)
                {
                    return result;
                }
            }
            if (NativeMethods.IsMaximized(hwnd))
                switch (result)
                {
                    case HitTest.Border:
                    case HitTest.BottomLeft:
                    case HitTest.Bottom:
                    case HitTest.BottomRight:
                    case HitTest.Right:
                    case HitTest.Top:
                    case HitTest.TopLeft:
                    case HitTest.TopRight:
                    case HitTest.Left:
                        //return HitTest.NOWHERE;
                        break;
                }
            return result;
        }

        #region Nested type: YAndHeight

        private struct YAndHeight
        {
            public readonly double Height;
            public readonly double Y;

            public YAndHeight(double y, double h)
            {
                Y = y;
                Height = h;
            }
        }

        #endregion
    }
}